package cc.autoapi.pucong.liteflow.design.service;

import cc.autoapi.pucong.liteflow.design.core.configuration.AuthorizationInterceptor;
import cc.autoapi.pucong.liteflow.design.core.exception.DesignException;
import cc.autoapi.pucong.liteflow.design.core.properties.LiteFlowDesignProperties;
import cc.autoapi.pucong.liteflow.design.mobal.*;
import cc.autoapi.pucong.liteflow.design.mobal.dto.*;
import cc.autoapi.pucong.liteflow.design.mobal.el.ApiDesignNode;
import cc.autoapi.pucong.liteflow.design.mobal.el.ApiNode;
import cc.autoapi.pucong.liteflow.design.mobal.el.ApiNodeRelation;
import cc.autoapi.pucong.liteflow.design.mobal.vo.*;
import cc.autoapi.pucong.liteflow.design.utils.*;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.springboot.LiteflowProperty;
import com.yomahub.liteflow.util.JsonUtil;
import cc.autoapi.pucong.liteflow.design.mobal.*;
import cc.autoapi.pucong.liteflow.design.mobal.dto.*;
import cc.autoapi.pucong.liteflow.design.mobal.vo.*;
import cc.autoapi.pucong.liteflow.design.utils.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

public class DesignServiceImpl implements DesignService {
    private static final Logger log = LoggerFactory.getLogger(DesignServiceImpl.class);
    private static SQLParserVO sqlParserVO;
    private LiteflowProperty liteflowConfig;
    private LiteFlowDesignProperties properties;
    private AuthorizationInterceptor authorizationInterceptor;

    public DesignServiceImpl(LiteflowProperty liteflowProperty, LiteFlowDesignProperties properties, AuthorizationInterceptor authorizationInterceptor) {
        this.liteflowConfig = liteflowProperty;
        this.properties = properties;
        this.authorizationInterceptor = authorizationInterceptor;
        if (MapUtil.isNotEmpty((liteflowConfig.getRuleSourceExtDataMap()))) {
            sqlParserVO = BeanUtil.toBean(liteflowConfig.getRuleSourceExtDataMap(), SQLParserVO.class,
                    CopyOptions.create());
        } else if (StrUtil.isNotBlank(liteflowConfig.getRuleSourceExtData())) {
            sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class);
        }
        if (Objects.isNull(sqlParserVO)) {
            throw new DesignException("rule-source-ext-data is empty");
        }
    }

    @Override
    public String generateUuid() {
        String result = "";
        synchronized (result) {
            result = java.util.UUID.randomUUID().toString();
        }
        return result.replaceAll("-", "");
    }

    @Override
    public BasePageVo<ChainPageVo> getChainListByPage(int pageNum, int pageSize, String searchKey) {
        Connection conn = null;
        try {
            conn = LiteFlowJdbcUtil.getConn(sqlParserVO);
        } catch (DesignException e) {
            log.warn("no liteflow datasource setting");
        }
        if (conn != null) {
            String chainTableId = properties.getChainTableIdField();
            String chainTableName = sqlParserVO.getChainTableName();
            String chainNameField = sqlParserVO.getChainNameField();
            String chainApplicationNameField = sqlParserVO.getChainApplicationNameField();
            String chainEnableField = sqlParserVO.getChainEnableField();

            String sql = StrUtil.format(SqlConstant.SQL_PATTERN_KEYWORD, chainTableName, chainApplicationNameField, chainNameField);
            if (StringUtils.isBlank(searchKey)) {
                sql = StrUtil.format(SqlConstant.SQL_PATTERN, chainTableName);
            }
            BasePageVo<ChainPageVo> voBasePageVo = new BasePageVo<>(pageSize, pageNum);
            List<Object> params = new ArrayList<>();
            if (StringUtils.isNotBlank(searchKey)) {
                params.add("%" + searchKey + "%");
                params.add("%" + searchKey + "%");
            }
            ExeRunSqlResultPage exeRunSqlResultPage = DatabaseUtils.runSQLByPage(pageNum, pageSize, sql, params, conn, false);
            LiteFlowJdbcUtil.close(conn, null, null);

            BasePageVo<Map<String, Object>> execResultMapPage = exeRunSqlResultPage.getExecResultMapPage();
            List<ChainPageVo> vos = new ArrayList<>();

            BasePageVo<Map<String, Object>> execResultMapPage1 = exeRunSqlResultPage.getExecResultMapPage();
            List<Map<String, Object>> records = execResultMapPage1.getRecords();
            if (records != null) {
                for (Map<String, Object> record : records) {
                    Object id = record.get(chainTableId.toLowerCase());
                    Object chainName = record.get(chainNameField.toLowerCase());
                    Object chainApplication = record.get(chainApplicationNameField.toLowerCase());
                    Object chainEnable = record.get(chainEnableField.toLowerCase());
                    ChainPageVo vo = new ChainPageVo();
                    if (id != null) {
                        vo.setId(id.toString());
                    } else {
                        throw new DesignException("chainTableId setting error");
                    }
                    if (chainName != null) {
                        vo.setChainName(chainName.toString());
                    }
                    if (chainApplication != null) {
                        vo.setApplicationName(chainApplication.toString());
                    }
                    if (chainEnable != null) {
                        vo.setChainEnable(Integer.parseInt(chainEnable.toString()));
                    }
                    vos.add(vo);
                }
            }
            voBasePageVo.setPageSize(execResultMapPage.getPageSize());
            voBasePageVo.setPages(execResultMapPage.getPageSize());
            voBasePageVo.setTotal(execResultMapPage.getTotal());
            voBasePageVo.setPageNum(execResultMapPage.getPageNum());
            voBasePageVo.setRecords(vos);
            return voBasePageVo;
        } else {
            BasePageVo<ChainPageVo> voBasePageVo = new BasePageVo<>(pageSize, pageNum);
            Map<String, Chain> chainMap = FlowBus.getChainMap();
            Set<String> chainMapKeySet = chainMap.keySet();

            List<String> chainMapKeys = new ArrayList<>(chainMapKeySet);
            // 内存分页
            List<String> results = ListUtil.page(pageNum, pageSize, chainMapKeys);
            List<ChainPageVo> vos = new ArrayList<>();
            for (String key : results) {
                Chain chain = chainMap.get(key);
                String id = chain.getChainId();
                String chainName = key;
                String chainApplication = "";
                Integer chainEnable = 1;

                ChainPageVo vo = new ChainPageVo();
                vo.setId(id);
                vo.setChainName(chainName);
                vo.setApplicationName(chainApplication.toString());
                vo.setChainEnable(chainEnable);
                vos.add(vo);
            }
            voBasePageVo.setPageSize(pageSize);
            voBasePageVo.setTotal(chainMapKeySet.size() + 0L);
            voBasePageVo.setPageNum(pageNum);
            voBasePageVo.setPages(Integer.parseInt(voBasePageVo.runPages() + ""));
            voBasePageVo.setRecords(vos);
            return voBasePageVo;
        }
    }

    @Override
    public ChainPageVo getChainInfoById(String chainId) {
        Connection conn = null;
        try {
            conn = LiteFlowJdbcUtil.getConn(sqlParserVO);
        } catch (DesignException e) {
            log.warn("no liteflow datasource setting");
        }
        if (conn != null) {
            String chainTableId = properties.getChainTableIdField();
            String chainTableName = sqlParserVO.getChainTableName();
            String chainNameField = sqlParserVO.getChainNameField();
            String chainApplicationNameField = sqlParserVO.getChainApplicationNameField();
            String chainEnableField = sqlParserVO.getChainEnableField();

            String sql = StrUtil.format(SqlConstant.SQL_PATTERN_ID, chainTableName, chainTableId);
            List<Object> params = new ArrayList<>();
            params.add(chainId);
            ExeSqlResult exeSqlResult = null;
            try {
                exeSqlResult = DatabaseUtils.execQuerySql(params, sql, conn, false);
            } catch (SQLException e) {
                log.error("执行异常", e);
                throw new DesignException("run error" + e.getMessage());
            } finally {
                LiteFlowJdbcUtil.close(conn, null, null);
            }

            List<Map<String, Object>> resultList = exeSqlResult.getResultList();
            ChainPageVo vo = new ChainPageVo();
            for (Map<String, Object> record : resultList) {
                Object id = record.get(chainTableId.toLowerCase());
                Object chainName = record.get(chainNameField.toLowerCase());
                Object chainApplication = record.get(chainApplicationNameField.toLowerCase());
                Object chainEnable = record.get(chainEnableField.toLowerCase());

                if (id != null) {
                    vo.setId(id.toString());
                } else {
                    throw new DesignException("chainTableId setting error");
                }
                if (chainName != null) {
                    vo.setChainName(chainName.toString());
                }
                if (chainApplication != null) {
                    vo.setApplicationName(chainApplication.toString());
                }
                if (chainEnable != null) {
                    vo.setChainEnable(Integer.parseInt(chainEnable.toString()));
                }
            }
            return vo;
        } else {
            Chain chain = FlowBus.getChain(chainId);
            String id = chain.getChainId();
            String chainName = chain.getChainId();
            String chainApplication = "";
            Integer chainEnable = 1;

            ChainPageVo vo = new ChainPageVo();
            vo.setId(id);
            vo.setChainName(chainName);
            vo.setApplicationName(chainApplication.toString());
            vo.setChainEnable(chainEnable);
            return vo;
        }
    }

    @Override
    public Boolean addChain(ChainInsertDto dto) {
        if (StringUtils.isBlank(dto.getApplicationName())) {
            throw new DesignException("ApplicationName can not be bank");
        }
        if (StringUtils.isBlank(dto.getChainName())) {
            throw new DesignException("ChainName can not be bank");
        }
        Connection conn = null;
        try {
            conn = LiteFlowJdbcUtil.getConn(sqlParserVO);
        } catch (DesignException e) {
            throw new DesignException("no liteflow datasource setting");
        }
        String chainTableIdField = properties.getChainTableIdField();
        Boolean chainTableIdAutoType = properties.getChainTableIdAutoType();
        String chainTableName = sqlParserVO.getChainTableName();
        String chainNameField = sqlParserVO.getChainNameField();
        String chainApplicationNameField = sqlParserVO.getChainApplicationNameField();
        String chainEnableField = sqlParserVO.getChainEnableField();

        Boolean tablePkIsNum = getTablePkIsNum(conn, chainTableName, chainTableIdField);
        System.out.println(tablePkIsNum);
        if (chainTableIdAutoType) {
            String sql = StrUtil.format(SqlConstant.SQL_INSERT_AUTO, chainTableName, chainApplicationNameField, chainNameField, chainEnableField);
            List<Object> params = new ArrayList<>();
            params.add(dto.getChainName());
            params.add(dto.getApplicationName());
            params.add(0);
            ExeSqlResult exeSqlResult = DatabaseUtils.execSql(conn, sql, params, false);
        } else {
            String sql = StrUtil.format(SqlConstant.SQL_INSERT, chainTableName, chainTableIdField, chainApplicationNameField, chainNameField, chainEnableField);
            List<Object> params = new ArrayList<>();
            params.add(generateUuid());
            params.add(dto.getChainName());
            params.add(dto.getApplicationName());
            params.add(0);
            ExeSqlResult exeSqlResult = DatabaseUtils.execSql(conn, sql, params, false);
        }

        LiteFlowJdbcUtil.close(conn, null, null);
        return true;
    }

    /**
     * 获取表的主键信息
     *
     * @param conn
     */
    private Boolean getTablePkIsNum(Connection conn, String tableName, String tableIdField) {
        try {
            DatabaseMetaData databaseMetaData = conn.getMetaData();
            ResultSet rs = databaseMetaData.getColumns(conn.getCatalog(), conn.getSchema(), tableName, tableIdField);
            while (rs.next()) {
                rs.getString("COLUMN_NAME");
                //numeric
                rs.getString("TYPE_NAME").toLowerCase();
                rs.getString("COLUMN_NAME").toLowerCase();
                rs.getString("REMARKS").toLowerCase();
            }
            LiteFlowJdbcUtil.close(null, null, rs);
        } catch (SQLException e) {
            log.error("获取表获取主键失败", e);
        }
        return true;
    }

    @Override
    public Boolean updateChainById(String chainId, ChainUpdateDto dto) {
        if (StringUtils.isBlank(dto.getApplicationName())) {
            throw new DesignException("ApplicationName can not be bank");
        }
        if (StringUtils.isBlank(dto.getChainName())) {
            throw new DesignException("ChainName can not be bank");
        }
        if (dto.getChainEnable() == null) {
            throw new DesignException("ChainEnable can not be bank");
        }
        Connection conn = null;
        try {
            conn = LiteFlowJdbcUtil.getConn(sqlParserVO);
        } catch (DesignException e) {
            throw new DesignException("no liteflow datasource setting");
        }
        String chainTableIdField = properties.getChainTableIdField();
        Boolean chainTableIdAutoType = properties.getChainTableIdAutoType();
        String chainTableName = sqlParserVO.getChainTableName();
        String chainNameField = sqlParserVO.getChainNameField();
        String chainApplicationNameField = sqlParserVO.getChainApplicationNameField();
        String chainEnableField = sqlParserVO.getChainEnableField();

        String sql = StrUtil.format(SqlConstant.SQL_UPDATE, chainTableName, chainApplicationNameField, chainNameField, chainEnableField, chainTableIdField);
        List<Object> params = new ArrayList<>();
        params.add(dto.getChainName());
        params.add(dto.getApplicationName());
        params.add(dto.getChainEnable());
        if (chainTableIdAutoType) {
            params.add(Integer.parseInt(chainId));
        } else {
            params.add(chainId);
        }
        ExeSqlResult exeSqlResult = DatabaseUtils.execSql(conn, sql, params, false);
        LiteFlowJdbcUtil.close(conn, null, null);
        return true;
    }

    @Override
    public Boolean updateChainElById(String chainId, ChainElUpdateDto dto) {
        if (StringUtils.isBlank(dto.getElData())) {
            throw new DesignException("ApplicationName can not be bank");
        }
        Connection conn = null;
        try {
            conn = LiteFlowJdbcUtil.getConn(sqlParserVO);
        } catch (DesignException e) {
            throw new DesignException("no liteflow datasource setting");
        }
        // 转化为el表达式
        List<ApiNodeRelation> addRelateList = new ArrayList<>();
        List<ApiNode> addNodeList = new ArrayList<>();
        ElDesignUtils.dealJson2List(dto.getElData(), addNodeList, addRelateList);
        String el = ElDesignUtils.getEl(addNodeList, addRelateList);

        String chainTableIdField = properties.getChainTableIdField();
        Boolean chainTableIdAutoType = properties.getChainTableIdAutoType();
        String chainTableName = sqlParserVO.getChainTableName();
        String elDataField = sqlParserVO.getElDataField();

        String sql = StrUtil.format(SqlConstant.SQL_UPDATE_ONE, chainTableName, elDataField, chainTableIdField);
        List<Object> params = new ArrayList<>();
        params.add(el);
        if (chainTableIdAutoType) {
            params.add(Integer.parseInt(chainId));
        } else {
            params.add(chainId);
        }
        ExeSqlResult exeSqlResult = DatabaseUtils.execSql(conn, sql, params, false);
        LiteFlowJdbcUtil.close(conn, null, null);
        return true;
    }

    @Override
    public List<NodeVo> getAllNodeList(String searchKey) {
        Map<String, Node> nodeMap = FlowBus.getNodeMap();
        Set<String> strings = nodeMap.keySet();
        List<NodeVo> vos = new ArrayList<>();
        // 默认节点
        NodeCodeEnum[] itemArr = new NodeCodeEnum[]{NodeCodeEnum.IF, NodeCodeEnum.SWITCH,
                NodeCodeEnum.FOR, NodeCodeEnum.WHILE, NodeCodeEnum.ITERATOR,
                NodeCodeEnum.PROCESS, NodeCodeEnum.BREAK, NodeCodeEnum.CATCH, NodeCodeEnum.CATCH_END};
        for (NodeCodeEnum nodeCodeEnum : itemArr) {
            NodeVo vo1 = new NodeVo();
            vo1.setId(nodeCodeEnum.getValue());
            vo1.setName(nodeCodeEnum.getValue());
            vo1.setNodeCode(nodeCodeEnum);
            vo1.setBaseNode(true);
            vos.add(vo1);
        }

        for (String key : strings) {
            Node node = nodeMap.get(key);
            if (StringUtils.isNotBlank(searchKey)) {
                if (node.getName().contains(searchKey)) {
                    NodeVo nodeVo = new NodeVo();
                    nodeVo.setId(node.getId());
                    nodeVo.setName(StringUtils.isBlank(node.getName()) ? node.getId() : node.getName());
                    nodeVo.setNodeCode(NodeCodeEnum.findType(node.getType()));
                    nodeVo.setLanguage(node.getLanguage());
                    nodeVo.setScriptData(node.getScript());
                    nodeVo.setBaseNode(false);

                    vos.add(nodeVo);
                }
            } else {
                NodeVo nodeVo = new NodeVo();
                nodeVo.setId(node.getId());
                nodeVo.setName(StringUtils.isBlank(node.getName()) ? node.getId() : node.getName());
                nodeVo.setNodeCode(NodeCodeEnum.findType(node.getType()));
                nodeVo.setLanguage(node.getLanguage());
                nodeVo.setScriptData(node.getScript());
                nodeVo.setBaseNode(false);
                vos.add(nodeVo);
            }
        }
        return vos;
    }

    @Override
    public String getChainElById(String chainId) {
        Connection conn = null;
        try {
            conn = LiteFlowJdbcUtil.getConn(sqlParserVO);
        } catch (DesignException e) {
            log.warn("no liteflow datasource setting");
        }
        if (conn != null) {
            String chainTableId = properties.getChainTableIdField();
            String chainTableName = sqlParserVO.getChainTableName();
            String elDataField = sqlParserVO.getElDataField();

            String sql = StrUtil.format(SqlConstant.SQL_PATTERN_ID, chainTableName, chainTableId);
            List<Object> params = new ArrayList<>();
            params.add(chainId);
            ExeSqlResult exeSqlResult = null;
            try {
                exeSqlResult = DatabaseUtils.execQuerySql(params, sql, conn, false);
            } catch (SQLException e) {
                log.error("执行异常", e);
                throw new DesignException("run error" + e.getMessage());
            } finally {
                LiteFlowJdbcUtil.close(conn, null, null);
            }

            List<Map<String, Object>> resultList = exeSqlResult.getResultList();
            String el = "";
            for (Map<String, Object> record : resultList) {
                Object elData = record.get(elDataField.toLowerCase());
                el = elData.toString();
            }
            return el;
        } else {
            Chain chain = FlowBus.getChain(chainId);
            return chain.getEl();
        }
    }

    @Override
    public ChainDesignVo getChainDesignJsonById(String chainId) {
        Connection conn = null;
        try {
            conn = LiteFlowJdbcUtil.getConn(sqlParserVO);
        } catch (DesignException e) {
            log.warn("no liteflow datasource setting");
        }
        if (conn != null) {
            String chainTableId = properties.getChainTableIdField();
            String chainTableName = sqlParserVO.getChainTableName();
            String elDataField = sqlParserVO.getElDataField();

            String sql = StrUtil.format(SqlConstant.SQL_PATTERN_ID, chainTableName, chainTableId);
            List<Object> params = new ArrayList<>();
            params.add(chainId);
            ExeSqlResult exeSqlResult = null;
            try {
                exeSqlResult = DatabaseUtils.execQuerySql(params, sql, conn, false);
            } catch (SQLException e) {
                log.error("执行异常", e);
                throw new DesignException("run error" + e.getMessage());
            } finally {
                LiteFlowJdbcUtil.close(conn, null, null);
            }

            List<Map<String, Object>> resultList = exeSqlResult.getResultList();
            Map<String, Object> stringObjectMap = resultList.get(0);
            Object elData = stringObjectMap.get(elDataField.toLowerCase());
            String el = "";
            if (elData != null) {
                el = elData.toString();
            }
            List<ApiNodeRelation> relateList = new ArrayList<>();
            List<ApiDesignNode> nodeList = new ArrayList<>();
            if (StringUtils.isNotBlank(el)) {
                ElDesignUtils.dealEl2List(el, relateList, nodeList);
            }

            ChainDesignVo vo = new ChainDesignVo();
            vo.setNodeList(nodeList);
            vo.setRelateList(relateList);
            return vo;
        } else {
            Chain chain = FlowBus.getChain(chainId);
            String el = chain.getEl();

            List<ApiNodeRelation> relateList = new ArrayList<>();
            List<ApiDesignNode> nodeList = new ArrayList<>();
            ElDesignUtils.dealEl2List(el, relateList, nodeList);

            ChainDesignVo vo = new ChainDesignVo();
            vo.setNodeList(nodeList);
            vo.setRelateList(relateList);
            return vo;
        }
    }

    @Override
    public HeaderDesignUser getLoginUser() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        Object attribute = request.getAttribute(Constants.ATTRIBUTE_DESIGN_USER);
        if (attribute == null) {
            return null;
        }
        HeaderDesignUser loginUser = (HeaderDesignUser) attribute;
        return loginUser;
    }

    @Override
    public List<NodeScriptVo> getScriptNodeList(String searchKey) {
        Connection conn = null;
        try {
            conn = LiteFlowJdbcUtil.getConn(sqlParserVO);
        } catch (DesignException e) {
            log.warn("no liteflow datasource setting");
        }
        if (conn != null) {
            String scriptIdField = sqlParserVO.getScriptIdField();
            Boolean scriptTableIdAutoType = properties.getScriptTableIdAutoType();
            String scriptTableName = sqlParserVO.getScriptTableName();
            String scriptNameField = sqlParserVO.getScriptNameField();
            String scriptApplicationNameField = sqlParserVO.getScriptApplicationNameField();
            String scriptTypeField = sqlParserVO.getScriptTypeField();
            String scriptEnableField = sqlParserVO.getScriptEnableField();
            String scriptLanguageField = sqlParserVO.getScriptLanguageField();
            String scriptDataField = sqlParserVO.getScriptDataField();

            String sql = StrUtil.format(SqlConstant.SQL_PATTERN_KEYWORD, scriptTableName, scriptNameField, scriptApplicationNameField);
            List<Object> params = new ArrayList<>();
            params.add("%" + searchKey + "%");
            params.add("%" + searchKey + "%");
            ExeSqlResult exeSqlResult = null;
            try {
                exeSqlResult = DatabaseUtils.execQuerySql(params, sql, conn, false);
            } catch (SQLException e) {
                log.error("查询数据失败", e);
                throw new DesignException("select error");
            } finally {
                LiteFlowJdbcUtil.close(conn, null, null);
            }

            List<Map<String, Object>> resultList = exeSqlResult.getResultList();
            List<NodeScriptVo> vos = new ArrayList<>();

            for (Map<String, Object> record : resultList) {
                Object id = record.get(scriptIdField.toLowerCase());
                Object scriptType = record.get(scriptTypeField.toLowerCase());
                Object scriptLanguage = record.get(scriptLanguageField.toLowerCase());
                Object scriptApplicationName = record.get(scriptApplicationNameField.toLowerCase());
                Object scriptName = record.get(scriptNameField.toLowerCase());
                Object scriptEnable = record.get(scriptEnableField.toLowerCase());
                Object scriptData = record.get(scriptDataField.toLowerCase());

                NodeScriptVo vo = new NodeScriptVo();
                if (id != null) {
                    vo.setId(id.toString());
                } else {
                    throw new DesignException("scriptTableId setting error");
                }
                if (scriptName != null) {
                    vo.setScriptName(scriptName.toString());
                }
                if (scriptApplicationName != null) {
                    vo.setApplicationName(scriptApplicationName.toString());
                }
                if (scriptType != null) {
                    vo.setScriptType(scriptType.toString());
                }
//                if (scriptData != null) {
//                    vo.setScriptData(scriptData.toString());
//                }
                if (scriptLanguage != null) {
                    vo.setScriptLanguage(scriptLanguage.toString());
                }
                if (scriptEnable != null) {
                    vo.setScriptEnable(Integer.parseInt(scriptEnable.toString()));
                }
                vos.add(vo);
            }
            return vos;
        } else {
            Map<String, Node> nodeMap = FlowBus.getNodeMap();
            Set<String> chainMapKeySet = nodeMap.keySet();

            List<String> chainMapKeys = new ArrayList<>(chainMapKeySet);
            List<NodeScriptVo> vos = new ArrayList<>();
            for (String key : chainMapKeys) {
                Node node = nodeMap.get(key);
                NodeTypeEnum type = node.getType();
                if (type == NodeTypeEnum.SCRIPT || type == NodeTypeEnum.BOOLEAN_SCRIPT ||
                        type == NodeTypeEnum.FOR_SCRIPT || type == NodeTypeEnum.SWITCH_SCRIPT) {

                    NodeScriptVo vo = new NodeScriptVo();
                    vo.setId(node.getId());
                    vo.setScriptName(node.getName());
                    vo.setApplicationName("");
                    vo.setScriptType(node.getType().getCode());
//                    vo.setScriptData(node.getScript());
                    vo.setScriptLanguage(node.getLanguage());
                    vo.setScriptEnable(1);
                    vos.add(vo);
                }
            }
            return vos;
        }
    }

    @Override
    public Boolean updateNodeDisableById(String nodeId) {
        Connection conn = LiteFlowJdbcUtil.getConn(sqlParserVO);
        if (conn == null) {
            throw new DesignException("node datasource set");
        }
        String scriptIdField = sqlParserVO.getScriptIdField();
        Boolean scriptTableIdAutoType = properties.getScriptTableIdAutoType();
        String scriptTableName = sqlParserVO.getScriptTableName();
        String scriptEnableField = sqlParserVO.getScriptEnableField();

        String sql = StrUtil.format(SqlConstant.SQL_UPDATE_ONE, scriptTableName, scriptEnableField, scriptIdField);
        List<Object> params = new ArrayList<>();
        params.add(0);
        if (scriptTableIdAutoType) {
            params.add(Integer.parseInt(nodeId));
        } else {
            params.add(nodeId);
        }
        ExeSqlResult exeSqlResult = DatabaseUtils.execSql(conn, sql, params, false);
        LiteFlowJdbcUtil.close(conn, null, null);
        return true;
    }

    @Override
    public Boolean updateNodeEnableById(String nodeId) {
        Connection conn = LiteFlowJdbcUtil.getConn(sqlParserVO);
        if (conn == null) {
            throw new DesignException("node datasource set");
        }

        String scriptIdField = sqlParserVO.getScriptIdField();
        Boolean scriptTableIdAutoType = properties.getScriptTableIdAutoType();
        String scriptTableName = sqlParserVO.getScriptTableName();
        String scriptEnableField = sqlParserVO.getScriptEnableField();

        String sql = StrUtil.format(SqlConstant.SQL_UPDATE_ONE, scriptTableName, scriptEnableField, scriptIdField);
        List<Object> params = new ArrayList<>();
        params.add(1);
        if (scriptTableIdAutoType) {
            params.add(Integer.parseInt(nodeId));
        } else {
            params.add(nodeId);
        }
        ExeSqlResult exeSqlResult = DatabaseUtils.execSql(conn, sql, params, false);
        LiteFlowJdbcUtil.close(conn, null, null);
        return true;
    }

    @Override
    public HeaderDesignUser login(UserLoginDto dto, HttpServletResponse response) {
        DesignUser user = authorizationInterceptor.loginCheck(dto.getLoginName(), dto.getPassword());
        Long loginExpire = properties.getLoginExpire();
        long l = loginExpire * 60 * 1000L;
        Date date = new Date();
        Date date1 = new Date(date.getTime() + l);
        HeaderDesignUser headerDesignUser = new HeaderDesignUser(user.getId(), user.getUsername(), date1, user.getToken());
        ObjectMapper objectMapper = new ObjectMapper();
        String userJson = "";
        try {
            userJson = objectMapper.writeValueAsString(headerDesignUser);
        } catch (JsonProcessingException e) {
            throw new DesignException("生成json失败");
        }
        String encrypt = AESEncryptUtil.encrypt(userJson);
        headerDesignUser.setToken(encrypt);
        response.setHeader(Constants.DESIGN_TOKEN_HEADER, encrypt);
        response.setHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, Constants.DESIGN_TOKEN_HEADER);
        return headerDesignUser;
    }

    @Override
    public List<ChainPageVo> getChainList(String searchKey) {
        Connection conn = null;
        try {
            conn = LiteFlowJdbcUtil.getConn(sqlParserVO);
        } catch (DesignException e) {
            log.warn("no liteflow datasource setting");
        }
        if (conn != null) {
            String chainTableId = properties.getChainTableIdField();
            String chainTableName = sqlParserVO.getChainTableName();
            String chainNameField = sqlParserVO.getChainNameField();
            String chainApplicationNameField = sqlParserVO.getChainApplicationNameField();
            String chainEnableField = sqlParserVO.getChainEnableField();

            String sql = StrUtil.format(SqlConstant.SQL_PATTERN_KEYWORD, chainTableName, chainApplicationNameField, chainNameField);
            if (StringUtils.isBlank(searchKey)) {
                sql = StrUtil.format(SqlConstant.SQL_PATTERN, chainTableName);
            }
            List<Object> params = new ArrayList<>();
            if (StringUtils.isNotBlank(searchKey)) {
                params.add("%" + searchKey + "%");
                params.add("%" + searchKey + "%");
            }
            ExeSqlResult exeSqlResult = null;
            try {
                exeSqlResult = DatabaseUtils.execQuerySql(params, sql, conn, false);
            } catch (SQLException e) {
                log.error("执行sql异常", e);
                throw new DesignException("执行sql异常" + e.getMessage());
            } finally {
                LiteFlowJdbcUtil.close(conn, null, null);
            }

            List<ChainPageVo> vos = new ArrayList<>();
            List<Map<String, Object>> records = exeSqlResult.getResultList();
            if (records != null) {
                for (Map<String, Object> record : records) {
                    Object id = record.get(chainTableId.toLowerCase());
                    Object chainName = record.get(chainNameField.toLowerCase());
                    Object chainApplication = record.get(chainApplicationNameField.toLowerCase());
                    Object chainEnable = record.get(chainEnableField.toLowerCase());
                    ChainPageVo vo = new ChainPageVo();
                    if (id != null) {
                        vo.setId(id.toString());
                    } else {
                        throw new DesignException("chainTableId setting error");
                    }
                    if (chainName != null) {
                        vo.setChainName(chainName.toString());
                    }
                    if (chainApplication != null) {
                        vo.setApplicationName(chainApplication.toString());
                    }
                    if (chainEnable != null) {
                        vo.setChainEnable(Integer.parseInt(chainEnable.toString()));
                    }
                    vos.add(vo);
                }
            }
            return vos;
        } else {
            Map<String, Chain> chainMap = FlowBus.getChainMap();
            Set<String> chainMapKeySet = chainMap.keySet();

            List<String> chainMapKeys = new ArrayList<>(chainMapKeySet);
            // 内存分页
            List<ChainPageVo> vos = new ArrayList<>();
            for (String key : chainMapKeys) {
                Chain chain = chainMap.get(key);
                String id = chain.getChainId();
                String chainName = key;
                String chainApplication = "";
                Integer chainEnable = 1;

                ChainPageVo vo = new ChainPageVo();
                vo.setId(id);
                vo.setChainName(chainName);
                vo.setApplicationName(chainApplication.toString());
                vo.setChainEnable(chainEnable);
                vos.add(vo);
            }
            return vos;
        }
    }

    @Override
    public Boolean needLogin() {
        Boolean requireLogin = properties.getRequireLogin();
        return requireLogin;
    }

    @Override
    public Boolean isLogin() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        Object attribute = request.getAttribute(Constants.ATTRIBUTE_DESIGN_USER);
        if (attribute == null) {
            return false;
        }
        HeaderDesignUser loginUser = (HeaderDesignUser) attribute;
        // 校验token
        Date expireTime = loginUser.getExpireTime();
        if (expireTime.before(new Date())) {
            return false;
        }
        return true;
    }

    @Override
    public Boolean loginOut() {
        // 清理头部的token
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        request.removeAttribute(Constants.DESIGN_TOKEN_HEADER);
        return true;
    }

    @Override
    public Boolean deleteChainById(String chainId) {
        Connection conn = LiteFlowJdbcUtil.getConn(sqlParserVO);

        String chainTableIdField = properties.getChainTableIdField();
        Boolean chainTableIdAutoType = properties.getChainTableIdAutoType();
        String chainTableName = sqlParserVO.getChainTableName();

        String sql = StrUtil.format(SqlConstant.SQL_DELETE, chainTableName, chainTableIdField);
        List<Object> params = new ArrayList<>();
        if (chainTableIdAutoType) {
            params.add(Integer.parseInt(chainId));
        } else {
            params.add(chainId);
        }
        ExeSqlResult exeSqlResult = DatabaseUtils.execSql(conn, sql, params, false);
        LiteFlowJdbcUtil.close(conn, null, null);
        return true;
    }

    @Override
    public Boolean updateChainEnableById(String chainId) {
        Connection conn = LiteFlowJdbcUtil.getConn(sqlParserVO);
        if (conn == null) {
            throw new DesignException("node datasource set");
        }

        String chainTableIdField = properties.getChainTableIdField();
        Boolean chainTableIdAutoType = properties.getChainTableIdAutoType();
        String chainTableName = sqlParserVO.getChainTableName();
        String chainEnableField = sqlParserVO.getChainEnableField();

        String sql = StrUtil.format(SqlConstant.SQL_UPDATE_ONE, chainTableName, chainEnableField, chainTableIdField);
        List<Object> params = new ArrayList<>();
        params.add(1);
        if (chainTableIdAutoType) {
            params.add(Integer.parseInt(chainId));
        } else {
            params.add(chainId);
        }
        ExeSqlResult exeSqlResult = DatabaseUtils.execSql(conn, sql, params, false);
        LiteFlowJdbcUtil.close(conn, null, null);
        return true;
    }

    @Override
    public Boolean updateChainDisableById(String chainId) {
        Connection conn = LiteFlowJdbcUtil.getConn(sqlParserVO);
        if (conn == null) {
            throw new DesignException("node datasource set");
        }

        String chainTableIdField = properties.getChainTableIdField();
        Boolean chainTableIdAutoType = properties.getChainTableIdAutoType();
        String chainTableName = sqlParserVO.getChainTableName();
        String chainEnableField = sqlParserVO.getChainEnableField();

        String sql = StrUtil.format(SqlConstant.SQL_UPDATE_ONE, chainTableName, chainEnableField, chainTableIdField);
        List<Object> params = new ArrayList<>();
        params.add(0);
        if (chainTableIdAutoType) {
            params.add(Integer.parseInt(chainId));
        } else {
            params.add(chainId);
        }
        ExeSqlResult exeSqlResult = DatabaseUtils.execSql(conn, sql, params, false);
        LiteFlowJdbcUtil.close(conn, null, null);
        return true;
    }

    @Override
    public Boolean addNode(NodeInsertDto dto) {
        if (StringUtils.isBlank(dto.getApplicationName())) {
            throw new DesignException("ApplicationName can not be bank");
        }
        if (StringUtils.isBlank(dto.getScriptType())) {
            throw new DesignException("ScriptType can not be bank");
        }
        if (StringUtils.isBlank(dto.getScriptName())) {
            throw new DesignException("ScriptName can not be bank");
        }
        if (StringUtils.isBlank(dto.getScriptLanguage())) {
            throw new DesignException("ScriptLanguage can not be bank");
        }
        if (dto.getScriptEnable() == null) {
            throw new DesignException("ChainEnable can not be bank");
        }
        Connection conn = null;
        try {
            conn = LiteFlowJdbcUtil.getConn(sqlParserVO);
        } catch (DesignException e) {
            throw new DesignException("no liteflow datasource setting");
        }
        String scriptIdField = sqlParserVO.getScriptIdField();
        Boolean scriptTableIdAutoType = properties.getScriptTableIdAutoType();
        String scriptTableName = sqlParserVO.getScriptTableName();
        String scriptNameField = sqlParserVO.getScriptNameField();
        String scriptApplicationNameField = sqlParserVO.getScriptApplicationNameField();
        String scriptTypeField = sqlParserVO.getScriptTypeField();
        String scriptEnableField = sqlParserVO.getScriptEnableField();
        String scriptLanguageField = sqlParserVO.getScriptLanguageField();
        String scriptDataField = sqlParserVO.getScriptDataField();

        if (scriptTableIdAutoType) {
            String sql = StrUtil.format(SqlConstant.SQL_NODE_INSERT_AUTO, scriptTableName, scriptNameField, scriptApplicationNameField, scriptTypeField, scriptEnableField, scriptLanguageField, scriptDataField);
            List<Object> params = new ArrayList<>();
            params.add(dto.getScriptName());
            params.add(dto.getApplicationName());
            params.add(dto.getScriptType());
            params.add(dto.getScriptEnable());
            params.add(dto.getScriptLanguage());
            params.add(getScriptDefault(dto.getScriptLanguage(), dto.getScriptType()));
            ExeSqlResult exeSqlResult = DatabaseUtils.execSql(conn, sql, params, false);
        } else {
            String sql = StrUtil.format(SqlConstant.SQL_NODE_INSERT, scriptTableName, scriptIdField, scriptNameField, scriptApplicationNameField, scriptTypeField, scriptEnableField, scriptLanguageField, scriptDataField);
            List<Object> params = new ArrayList<>();
            params.add(generateUuid());
            params.add(dto.getScriptName());
            params.add(dto.getApplicationName());
            params.add(dto.getScriptType());
            params.add(dto.getScriptEnable());
            params.add(dto.getScriptLanguage());
            params.add(getScriptDefault(dto.getScriptLanguage(), dto.getScriptType()));
            ExeSqlResult exeSqlResult = DatabaseUtils.execSql(conn, sql, params, false);
        }

        LiteFlowJdbcUtil.close(conn, null, null);
        return true;
    }

    private String getScriptDefault(String scriptLanguage, String scriptType) {
        String content = "";
        /**
         * groovy
         * js
         * python
         * qlexpress
         * lua
         * aviator
         * java
         * kotlin
         *
         * script
         * switch_script
         * boolean_script
         * for_script
         */
        switch (scriptLanguage) {
            case "groovy":
                content = "def a = 3\n" +
                        "    def b = 2";
                break;
            case "js":
                content = " var a=3;\n" +
                        "        var b=2;";
                break;
            case "python":
                content = " a=6\n" +
                        "        b=10";
                break;
            case "qlexpress":
                content = "   a=3;\n" +
                        "        b=2;";
                break;
            case "lua":
                content = "  local a=6\n" +
                        "        local b=10";
                break;
            case "aviator":
                content = "  a = 2;\n" +
                        "        b = 3;";
                break;
            case "java":
                content = "int v1 = 2;\n" +
                        "            int v2 = 3;";
                break;
            case "kotlin":
                content = "    var a = 2\n" +
                        "    var b = 3";
                break;
        }
        return content;
    }

    @Override
    public Boolean updateNodeById(String nodeId, NodeUpdateDto dto) {
        if (StringUtils.isBlank(dto.getApplicationName())) {
            throw new DesignException("ApplicationName can not be bank");
        }
        if (StringUtils.isBlank(dto.getScriptType())) {
            throw new DesignException("ScriptType can not be bank");
        }
        if (StringUtils.isBlank(dto.getScriptName())) {
            throw new DesignException("ScriptName can not be bank");
        }
        if (StringUtils.isBlank(dto.getScriptLanguage())) {
            throw new DesignException("ScriptLanguage can not be bank");
        }
        if (dto.getScriptEnable() == null) {
            throw new DesignException("ChainEnable can not be bank");
        }
        Connection conn = null;
        try {
            conn = LiteFlowJdbcUtil.getConn(sqlParserVO);
        } catch (DesignException e) {
            throw new DesignException("no liteflow datasource setting");
        }
        String scriptIdField = sqlParserVO.getScriptIdField();
        Boolean scriptTableIdAutoType = properties.getScriptTableIdAutoType();
        String scriptTableName = sqlParserVO.getScriptTableName();
        String scriptNameField = sqlParserVO.getScriptNameField();
        String scriptApplicationNameField = sqlParserVO.getScriptApplicationNameField();
        String scriptTypeField = sqlParserVO.getScriptTypeField();
        String scriptEnableField = sqlParserVO.getScriptEnableField();
        String scriptLanguageField = sqlParserVO.getScriptLanguageField();

        String sql = StrUtil.format(SqlConstant.SQL_NODE_UPDATE, scriptTableName, scriptNameField, scriptApplicationNameField, scriptTypeField, scriptEnableField, scriptLanguageField, scriptIdField);
        List<Object> params = new ArrayList<>();
        params.add(dto.getScriptName());
        params.add(dto.getApplicationName());
        params.add(dto.getScriptType());
        params.add(dto.getScriptEnable());
        params.add(dto.getScriptLanguage());
        if (scriptTableIdAutoType) {
            params.add(Integer.parseInt(nodeId));
        } else {
            params.add(nodeId);
        }
        ExeSqlResult exeSqlResult = DatabaseUtils.execSql(conn, sql, params, false);
        LiteFlowJdbcUtil.close(conn, null, null);
        return true;
    }

    @Override
    public Boolean updateNodeScriptById(String nodeId, NodeScriptUpdateDto dto) {
        if (StringUtils.isBlank(dto.getScript())) {
            throw new DesignException("script can not be bank");
        }
        Connection conn = null;
        try {
            conn = LiteFlowJdbcUtil.getConn(sqlParserVO);
        } catch (DesignException e) {
            throw new DesignException("no liteflow datasource setting");
        }
        String scriptIdField = sqlParserVO.getScriptIdField();
        Boolean scriptTableIdAutoType = properties.getScriptTableIdAutoType();
        String scriptTableName = sqlParserVO.getScriptTableName();
        String scriptDataField = sqlParserVO.getScriptDataField();

        String sql = StrUtil.format(SqlConstant.SQL_UPDATE_ONE, scriptTableName, scriptDataField, scriptIdField);
        List<Object> params = new ArrayList<>();
        params.add(dto.getScript());
        if (scriptTableIdAutoType) {
            params.add(Integer.parseInt(nodeId));
        } else {
            params.add(nodeId);
        }
        ExeSqlResult exeSqlResult = DatabaseUtils.execSql(conn, sql, params, false);
        LiteFlowJdbcUtil.close(conn, null, null);
        return true;
    }

    @Override
    public Boolean deleteNodeById(String nodeId) {
        Connection conn = LiteFlowJdbcUtil.getConn(sqlParserVO);

        String scriptIdField = sqlParserVO.getScriptIdField();
        Boolean scriptTableIdAutoType = properties.getScriptTableIdAutoType();
        String scriptTableName = sqlParserVO.getScriptTableName();

        String sql = StrUtil.format(SqlConstant.SQL_DELETE, scriptTableName, scriptIdField);
        List<Object> params = new ArrayList<>();
        if (scriptTableIdAutoType) {
            params.add(Integer.parseInt(nodeId));
        } else {
            params.add(nodeId);
        }
        ExeSqlResult exeSqlResult = DatabaseUtils.execSql(conn, sql, params, false);
        LiteFlowJdbcUtil.close(conn, null, null);
        return true;
    }

    @Override
    public NodeScriptVo getNodeInfoById(String nodeId) {
        Connection conn = null;
        try {
            conn = LiteFlowJdbcUtil.getConn(sqlParserVO);
        } catch (DesignException e) {
            log.warn("no liteflow datasource setting");
        }
        if (conn != null) {
            String scriptIdField = sqlParserVO.getScriptIdField();
            Boolean scriptTableIdAutoType = properties.getScriptTableIdAutoType();
            String scriptTableName = sqlParserVO.getScriptTableName();
            String scriptNameField = sqlParserVO.getScriptNameField();
            String scriptApplicationNameField = sqlParserVO.getScriptApplicationNameField();
            String scriptTypeField = sqlParserVO.getScriptTypeField();
            String scriptEnableField = sqlParserVO.getScriptEnableField();
            String scriptLanguageField = sqlParserVO.getScriptLanguageField();
            String scriptDataField = sqlParserVO.getScriptDataField();

            String sql = StrUtil.format(SqlConstant.SQL_PATTERN_ID, scriptTableName, scriptIdField);
            List<Object> params = new ArrayList<>();
            params.add(nodeId);
            ExeSqlResult exeSqlResult = null;
            try {
                exeSqlResult = DatabaseUtils.execQuerySql(params, sql, conn, false);
            } catch (SQLException e) {
                log.error("查询数据失败", e);
                throw new DesignException("select error");
            }
            LiteFlowJdbcUtil.close(conn, null, null);

            List<Map<String, Object>> resultList = exeSqlResult.getResultList();
            NodeScriptVo vo = new NodeScriptVo();
            for (Map<String, Object> record : resultList) {
                Object id = record.get(scriptIdField.toLowerCase());
                Object scriptType = record.get(scriptTypeField.toLowerCase());
                Object scriptLanguage = record.get(scriptLanguageField.toLowerCase());
                Object scriptApplicationName = record.get(scriptApplicationNameField.toLowerCase());
                Object scriptName = record.get(scriptNameField.toLowerCase());
                Object scriptEnable = record.get(scriptEnableField.toLowerCase());
                Object scriptData = record.get(scriptDataField.toLowerCase());

                if (id != null) {
                    vo.setId(id.toString());
                } else {
                    throw new DesignException("scriptTableId setting error");
                }
                if (scriptName != null) {
                    vo.setScriptName(scriptName.toString());
                }
                if (scriptApplicationName != null) {
                    vo.setApplicationName(scriptApplicationName.toString());
                }
                if (scriptType != null) {
                    vo.setScriptType(scriptType.toString());
                }
                if (scriptData != null) {
                    vo.setScriptData(scriptData.toString());
                }
                if (scriptLanguage != null) {
                    vo.setScriptLanguage(scriptLanguage.toString());
                }
                if (scriptEnable != null) {
                    vo.setScriptEnable(Integer.parseInt(scriptEnable.toString()));
                }
            }
            return vo;
        } else {
            Map<String, Node> nodeMap = FlowBus.getNodeMap();
            Set<String> chainMapKeySet = nodeMap.keySet();

            List<String> chainMapKeys = new ArrayList<>(chainMapKeySet);
            NodeScriptVo vo = new NodeScriptVo();
            for (String key : chainMapKeys) {
                Node node = nodeMap.get(key);
                NodeTypeEnum type = node.getType();
                if (type == NodeTypeEnum.SCRIPT || type == NodeTypeEnum.BOOLEAN_SCRIPT ||
                        type == NodeTypeEnum.FOR_SCRIPT || type == NodeTypeEnum.SWITCH_SCRIPT) {

                    vo.setId(node.getId());
                    vo.setScriptName(node.getName());
                    vo.setApplicationName("");
                    vo.setScriptType(node.getType().getCode());
                    vo.setScriptData(node.getScript());
                    vo.setScriptLanguage(node.getLanguage());
                    vo.setScriptEnable(1);
                }
            }
            return vo;
        }
    }
}
